package com.jiangchao.mimidao.framework.aop;

import com.jiangchao.mimidao.framework.annotation.ResultType;
import com.jiangchao.mimidao.framework.annotation.Sql;
import com.jiangchao.mimidao.framework.constant.MimiDaoConstant;
import com.jiangchao.mimidao.framework.util.FreemarkerParseFactory;
import com.jiangchao.mimidao.framework.util.ParameterNameUtils;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ognl.Ognl;
import ognl.OgnlException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.StringUtils;

public class MimiDaoHandler implements InvocationHandler {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Autowired
  private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

  private boolean formatSql = false;

  private boolean showSql = false;

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//    返回结果
    Object returnObj = null;
//    SQL模板
    String templateSql = "";
//    SQL参数
    Map<String, Object> sqlParamsMap = new HashMap<String, Object>();
//    step 1 转载sql模板及其所需参数
    sqlParamsMap = initDaoMetaParamData(method, args);
    templateSql = initDaoMetaSqlData(method, args);
    // Step 2 解析SQL模板，返回可执行SQL
    String executeSql = parseSqlTemplate(method, templateSql, sqlParamsMap);
    // Step 3 组装SQL占位符参数
    Map<String, Object> sqlMap = initPlaceholderSqlParam(executeSql, sqlParamsMap);
//    step 4 获得返回值类型
    returnObj = getReturnMinidaoResult(method, executeSql, sqlMap);
    return returnObj;
  }

  @SuppressWarnings("unchecked")
  private Object getReturnMinidaoResult(Method method, String executeSql,
      Map<String, Object> sqlMap) {
//    step 1 判断是否为查询语句（查询语句有返回值，非查询语句没有返回值）
    if (checkActiveSql(executeSql)) {
      if (sqlMap != null) {
        return namedParameterJdbcTemplate.update(executeSql, sqlMap);
      } else {
        return jdbcTemplate.update(executeSql);
      }
    } else {
// 如果是查詢操作
      Class<?> returnType = method.getReturnType();
//      判断是否是基本类型
      if (returnType.isPrimitive()) {
//        此时返回值只可能是数字类型
        Number number = namedParameterJdbcTemplate
            .queryForObject(executeSql, sqlMap, BigDecimal.class);
        if ("int".equals(returnType.getCanonicalName())) {
          return number == null ? 0 : number.intValue();
        }
        if ("long".equals(returnType.getCanonicalName())) {
          return number == null ? 0 : number.longValue();
        }
        if ("double".equals(returnType.getCanonicalName())) {
          return number == null ? 0 : number.doubleValue();
        }
        if ("float".equals(returnType.getCanonicalName())) {
          return number == null ? 0 : number.floatValue();
        }
      } else if (returnType.isAssignableFrom(List.class)) {

        List list = new ArrayList();
        if (sqlMap != null) {
          return namedParameterJdbcTemplate.queryForList(executeSql, sqlMap);
        } else {
          return jdbcTemplate.queryForList(executeSql);
        }
      } else if (returnType.isAssignableFrom(String.class)) {
        if (sqlMap != null) {
          return namedParameterJdbcTemplate.queryForObject(executeSql, sqlMap, String.class);
        } else {
          return jdbcTemplate.queryForObject(executeSql, String.class);
        }
      } else {
        // 对象类型
        RowMapper<?> rm = BeanPropertyRowMapper.newInstance(returnType);
        if (sqlMap != null) {
          Object object = null;
          try {
            namedParameterJdbcTemplate.queryForObject(executeSql, sqlMap, rm);
            return object;
          } catch (Exception e) {
            return null;
          }
        } else {
          return jdbcTemplate.queryForObject(executeSql, rm);
        }
      }
    }

    return null;
  }

  private boolean checkActiveSql(String executeSql) {
    executeSql = executeSql.trim().toLowerCase();
    String keys[] = MimiDaoConstant.INF_METHOD_ACTIVE.split(",");
    for (String s : keys) {
      if (executeSql.startsWith(s)) {
        return true;
      }
    }
    return false;
  }

  private Map<String, Object> initPlaceholderSqlParam(String executeSql,
      Map<String, Object> sqlParamsMap) throws OgnlException {
    Map<String, Object> map = new HashMap<String, Object>();
    //update-begin---author:scott----date:20160906------for:参数不支持下划线解决--------
    String regEx = ":[ tnx0Bfr]*[0-9a-z.A-Z_]+"; // 表示以：开头，[0-9或者.或者A-Z大小都写]的任意字符，超过一个
    //update-begin---author:scott----date:20160906------for:参数不支持下划线解决--------
    Pattern pat = Pattern.compile(regEx);
    Matcher m = pat.matcher(executeSql);
    while (m.find()) {
      System.out
          .println(" Match [" + m.group() + "] at positions " + m.start() + "-" + (m.end() - 1));
      String ognl_key = m.group().replace(":", "").trim();
      System.out.println(" --- minidao --- 解析参数 --- " + ognl_key);
      map.put(ognl_key, Ognl.getValue(ognl_key, sqlParamsMap));
    }
    return map;
  }

  private String parseSqlTemplate(Method method, String templateSql,
      Map<String, Object> sqlParamsMap) {

    String executeSql = "";
//    判断是否需要通过freemarker模板进行加载
    if (StringUtils.isEmpty(templateSql)) {
      String sqlTempletPath =
          method.getDeclaringClass().getName().replace(".", "/").replace("/dao/", "/sql/") + "_"
              + method.getName() + ".sql";
      try {
        if (!FreemarkerParseFactory.isExistTemplate(sqlTempletPath)) {
          sqlTempletPath =
              method.getDeclaringClass().getName().replace(".", "/") + "_" + method.getName()
                  + ".sql";
        }
      } catch (IOException e) {
        System.err.println("io异常");
        e.printStackTrace();
      }
      try {
        executeSql = FreemarkerParseFactory.parseTemplate(sqlTempletPath, sqlParamsMap);
      } catch (IOException e) {
        e.printStackTrace();
      } catch (TemplateException e) {
        e.printStackTrace();
      }
    } else {
      try {
        executeSql = FreemarkerParseFactory.parseTemplateContent(templateSql, sqlParamsMap);
      } catch (IOException e) {
        System.err.println("IO异常");
        e.printStackTrace();
      } catch (TemplateException e) {
        System.err.println("模板解析异常");
        e.printStackTrace();
      }
    }

    return executeSql;
  }

  private Map<String, Object> initDaoMetaParamData(Method method,
      Object[] args) {
    Map<String, Object> sqlParamsMap = new HashMap<String, Object>();
    //    step 1 获取对应的sql参数。
//    如果参数个数大于0，则必须使用@Param注解，并通过该注解获取对应的sql参数
    if (args != null && args.length > 0) {
      sqlParamsMap = ParameterNameUtils.getMethodParameterNamesByAnnotation(method, args);
      return sqlParamsMap;
    }
    return sqlParamsMap;
  }

  private String initDaoMetaSqlData(Method method,
      Object[] args) {
    String templateSql = "";

//    step 2 利用@Sql获取对应的sql模板
    if (method.isAnnotationPresent(Sql.class)) {
      Sql sql = method.getAnnotation(Sql.class);
      if (!StringUtils.isEmpty(sql.value())) {
        templateSql = sql.value();
      }
    }
    return templateSql;
  }

  public boolean isFormatSql() {
    return formatSql;
  }

  public void setFormatSql(boolean formatSql) {
    this.formatSql = formatSql;
  }

  public boolean isShowSql() {
    return showSql;
  }

  public void setShowSql(boolean showSql) {
    this.showSql = showSql;
  }
}
